/**
 *    @file       basecommand.h
 *
 *    @brief      
 *    @details
 *
 *    @version    1.0
 *    @date       02/17/2014 10:15:55 AM
 *
 *    @author     Theophilus (), wangtf418@gmail.com 
 */
#ifndef _SF_BASECOMMAND_H_
#define _SF_BASECOMMAND_H_
#include "srvdef.h"
#include "argument.h"
#include "buffermgr.h"

//there is no need to inherit from cmdhead, it's just a guide to define command head
struct cmdhead
{
public:
	cmdhead() {}
	virtual ~cmdhead() {}

public:
	virtual bf_int_t encode(buffermgr &bufmgr) = 0;
	virtual bf_int_t decode(buffermgr &bufmgr) = 0;
	virtual bf_uint32_t get_size() = 0;
	virtual void set_bodylen(bf_uint32_t bodylen) = 0;
	virtual bf_int_t verify_bodylen(bf_uint32_t buflen) {return BF_OK;}
	virtual bf_uint32_t get_cmdlen() = 0;
};

//there is no need to inherit from cmdhead, it's just a guide to define command body
struct cmdbody
{
public:
	cmdbody () {}
	virtual ~cmdbody() {}

public:
	virtual bf_int_t encode(buffermgr &bufmgr) = 0;
	virtual bf_int_t decode(buffermgr &bufmgr) = 0;
	virtual bf_uint32_t get_size() = 0;
};

class command
{
public:
	command() {}
	virtual ~command() {}
	enum cmd_process_errcode_t
	{
		ERR_ENCODE = 0,
		ERR_DECODE = 1,
		ERR_PROC_ENCODE_CMD = 2,
		ERR_PROC_DECODE_CMD = 3,
		ERR_PROCESS = 4,
		ERR_OTHER = 5
	};

public:
	virtual bf_int_t process() = 0;

public:	//for encode
	virtual bf_char_t * get_buf() = 0;
	virtual bf_uint32_t get_buflen() = 0;
	virtual void detach_buf() = 0;

public:	//for decode
	virtual void attach_buf(bf_char_t *buf, bf_uint32_t buflen) = 0;
	virtual void reset_buf(bf_char_t *buf, bf_uint32_t buflen) = 0;
	virtual bf_int_t get_errcode() {return 0;}
	
protected:
	virtual bf_int_t process_cmd() = 0;
	virtual bf_int_t encode() = 0;
	virtual bf_int_t decode() = 0;
};

class commandhelper
{
public:
	commandhelper(command *cmd) : m_cmd(cmd) {}
	~commandhelper() {if (m_cmd) delete m_cmd;}
private:
	command *m_cmd;
};

template<typename _Tres_cmdhead, typename _Tres_cmdbody>
class encode_cmd : public command
{
public:
	encode_cmd() : m_errcode(0) {}
	virtual ~encode_cmd() {}

public:
	virtual bf_int_t process()
	{
		bf_int_t ret = process_cmd();
		if (ret != BF_OK)
		{
			m_errcode = ERR_PROC_ENCODE_CMD;
			return BF_ERROR;
		}
		ret = encode();
		if (ret != BF_OK)
		{
			m_errcode = ERR_ENCODE;
			return BF_ERROR;
		}
		return BF_OK;
	}

public:	//for encode
	virtual bf_char_t * get_buf() {return m_buf.get_buf();}
	virtual bf_uint32_t get_buflen() {return m_buf.get_buflen();}
	virtual void detach_buf() {m_buf.detach_buf();}

public:	//for decode
	virtual void attach_buf(bf_char_t *buf, bf_uint32_t buflen) {return;}
	virtual void reset_buf(bf_char_t *buf, bf_uint32_t buflen) {return;}
	virtual bf_int_t get_errcode() {return m_errcode;}

protected:
	virtual bf_int_t process_cmd() = 0;

	virtual bf_int_t encode()
	{
		m_cmdhead.set_bodylen(m_cmdbody.get_size());
		bf_uint32_t buflen = m_cmdhead.get_cmdlen();
		if (buflen == 0)
		{
			return BF_ERROR;
		}
		bf_char_t *buf = new bf_char_t[buflen];
		m_buf.attach_buf(buf, buflen);

		bf_int_t ret = m_cmdhead.encode(m_buf);
		if (ret != BF_OK)
		{
			return BF_ERROR;
		}
		ret = m_cmdbody.encode(m_buf);
		if (ret != BF_OK)
		{
			return BF_ERROR;
		}
		return BF_OK;
	}

	virtual bf_int_t decode()
	{
		return BF_OK;
	}

protected:
	bf_int_t m_errcode;
	buffermgr m_buf;
	_Tres_cmdhead m_cmdhead;
	_Tres_cmdbody m_cmdbody;
};

template<typename _Treq_cmdhead, typename _Treq_cmdbody>
class decode_cmd : public command
{
public:
	decode_cmd(bf_char_t *buf, bf_uint32_t buflen) : m_errcode(0), m_buf(buf, buflen) {}
	virtual ~decode_cmd() {}

public:
	virtual bf_int_t process()
	{
		bf_int_t ret = decode();
		if (ret != BF_OK)
		{
			m_errcode = ERR_DECODE;
			return BF_ERROR;
		}
		ret = process_cmd();
		if (ret != BF_OK)
		{
			m_errcode = ERR_PROC_DECODE_CMD;
			return BF_ERROR;
		}
		return BF_OK;
	}

public:	//for encode
	virtual bf_char_t * get_buf() {return NULL;}
	virtual bf_uint32_t get_buflen() {return 0;}
	virtual void detach_buf() {return;}

public:	//for decode
	virtual void attach_buf(bf_char_t *buf, bf_uint32_t buflen) {m_buf.attach_buf(buf, buflen);}
	virtual void reset_buf(bf_char_t *buf, bf_uint32_t buflen) {return m_buf.reset_buf(buf, buflen);}
	virtual bf_int_t get_errcode() {return m_errcode;}

protected:
	virtual bf_int_t process_cmd() = 0;

	virtual bf_int_t encode()
	{
		return BF_OK;
	}

	virtual bf_int_t decode()
	{
		bf_int_t ret = m_cmdhead.decode(m_buf);
		if (ret != BF_OK)
		{
			return BF_ERROR;
		}
		if (m_cmdhead.verify_bodylen(m_buf.get_buflen()) != BF_OK)
		{
			return BF_ERROR;
		}
		ret = m_cmdbody.decode(m_buf);
		if (ret != BF_OK)
		{
			return BF_ERROR;
		}
		return BF_OK;
	}

protected:
	bf_int_t m_errcode;
	buffermgr m_buf;
	_Treq_cmdhead m_cmdhead;
	_Treq_cmdbody m_cmdbody;
};

template<typename _Treq_cmdhead, typename _Treq_cmdbody, typename _Tres_cmdhead, typename _Tres_cmdbody>
class basecmd : public command
{
public:
	basecmd(bf_char_t *buf, bf_uint32_t buflen) : m_req_buf(buf, buflen), m_errcode(0) {}
	virtual ~basecmd() {}

public:
	virtual bf_int_t process()
	{
		bf_int_t ret = decode();
		if (ret != BF_OK)
		{
			m_errcode = ERR_DECODE;
			return BF_ERROR;
		}

		ret = process_cmd();
		if (ret != BF_OK)
		{
			m_errcode = ERR_PROCESS;
			return BF_ERROR;
		}

		ret = encode();
		if (ret != BF_OK)
		{
			m_errcode = ERR_ENCODE;
			return BF_ERROR;
		}
		return BF_OK;
	}

public:	//for encode
	virtual bf_char_t * get_buf() {return m_res_buf.get_buf();}
	virtual bf_uint32_t get_buflen() {return m_res_buf.get_buflen();}
	virtual void detach_buf() {m_res_buf.detach_buf();}

public:	//for decode
	virtual void attach_buf(bf_char_t *buf, bf_uint32_t buflen) {m_req_buf.attach_buf(buf, buflen);}
	virtual void reset_buf(bf_char_t *buf, bf_uint32_t buflen) {return m_req_buf.reset_buf(buf, buflen);}
	virtual bf_int_t get_errcode() {return m_errcode;}

protected:
	virtual bf_int_t process_cmd() = 0;

	virtual bf_int_t encode()
	{
		m_res_cmdhead.set_bodylen(m_res_cmdbody.get_size());
		bf_uint32_t buflen = m_res_cmdhead.get_cmdlen();
		if (buflen == 0)
		{
			return BF_ERROR;
		}
		bf_char_t *buf = new bf_char_t[buflen];
		m_res_buf.attach_buf(buf, buflen);

		bf_int_t ret = m_res_cmdhead.encode(m_res_buf);
		if (ret != BF_OK)
		{
			return BF_ERROR;
		}
		ret = m_res_cmdbody.encode(m_res_buf);
		if (ret != BF_OK)
		{
			return BF_ERROR;
		}
		return BF_OK;
	}

	virtual bf_int_t decode()
	{
		bf_int_t ret = m_req_cmdhead.decode(m_req_buf);
		if (ret != BF_OK)
		{
			return BF_ERROR;
		}
		if (m_req_cmdhead.verify_bodylen(m_req_buf.get_buflen()) != BF_OK)
		{
			return BF_ERROR;
		}
		ret = m_req_cmdbody.decode(m_req_buf);
		if (ret != BF_OK)
		{
			return BF_ERROR;
		}
		return BF_OK;
	}

protected:
	buffermgr m_req_buf;
	buffermgr m_res_buf;
	_Treq_cmdhead m_req_cmdhead;
	_Treq_cmdbody m_req_cmdbody;
	_Tres_cmdhead m_res_cmdhead;
	_Tres_cmdbody m_res_cmdbody;
	bf_int_t m_errcode;
};

#endif

